/* Copyright  (C) 2010-2017 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (rwav.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h> /* ptrdiff_t on osx */
#include <stdlib.h>
#include <string.h>

#include <formats/rwav.h>

enum
{
   ITER_BEGIN,
   ITER_COPY_SAMPLES,
   ITER_COPY_SAMPLES_8,
   ITER_COPY_SAMPLES_16
};

void rwav_init(rwav_iterator_t* iter, rwav_t* out, const void* buf, size_t size)
{
   iter->out = out;
   iter->data = (const uint8_t*)buf;
   iter->size = size;
   iter->step = ITER_BEGIN;
   
   out->samples = NULL;
}

int rwav_iterate(rwav_iterator_t *iter)
{
   rwav_t *rwav = iter->out;
   const uint8_t *data = iter->data;
   uint16_t *u16;
   void *samples;
   size_t s;
   
   switch (iter->step)
   {
   case ITER_BEGIN:
      if (iter->size < 44)
         return RWAV_ITERATE_ERROR; /* buffer is smaller than an empty wave file */
      
      if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F')
         return RWAV_ITERATE_ERROR;
     
      if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E')
         return RWAV_ITERATE_ERROR;

      if (data[12] != 'f' || data[13] != 'm' || data[14] != 't' || data[15] != ' ')
         return RWAV_ITERATE_ERROR; /* we don't support non-PCM or compressed data */
      
      if (data[16] != 16 || data[17] != 0 || data[18] != 0 || data[19] != 0)
         return RWAV_ITERATE_ERROR;
      
      if (data[20] != 1 || data[21] != 0)
         return RWAV_ITERATE_ERROR; /* we don't support non-PCM or compressed data */

      if (data[36] != 'd' || data[37] != 'a' || data[38] != 't' || data[39] != 'a')
         return RWAV_ITERATE_ERROR;
      
      rwav->bitspersample = data[34] | data[35] << 8;
      
      if (rwav->bitspersample != 8 && rwav->bitspersample != 16)
         return RWAV_ITERATE_ERROR; /* we only support 8 and 16 bps */
      
      rwav->subchunk2size = data[40] | data[41] << 8 | data[42] << 16 | data[43] << 24;
      
      if (rwav->subchunk2size > iter->size - 44)
         return RWAV_ITERATE_ERROR; /* too few bytes in buffer */

      samples = malloc(rwav->subchunk2size);
      
      if (samples == NULL)
         return RWAV_ITERATE_ERROR;
      
      rwav->numchannels = data[22] | data[23] << 8;
      rwav->numsamples = rwav->subchunk2size * 8 / rwav->bitspersample / rwav->numchannels;
      rwav->samplerate = data[24] | data[25] << 8 | data[26] << 16 | data[27] << 24;
      rwav->samples = samples;
      
      iter->step = ITER_COPY_SAMPLES;
      return RWAV_ITERATE_MORE;
   
   case ITER_COPY_SAMPLES:
      iter->i = 0;
      
      if (rwav->bitspersample == 8)
      {
         iter->step = ITER_COPY_SAMPLES_8;
         
   case ITER_COPY_SAMPLES_8:
         s = rwav->subchunk2size - iter->i;
         
         if (s > RWAV_ITERATE_BUF_SIZE)
            s = RWAV_ITERATE_BUF_SIZE;
         
         memcpy((void*)rwav->samples, (void *)(iter->data + 44 + iter->i), s);
         printf("copied %lu\n", s);
         iter->i += s;
         
         return iter->i < rwav->subchunk2size ? RWAV_ITERATE_MORE : RWAV_ITERATE_DONE;
      }
      else
      {
         iter->step = ITER_COPY_SAMPLES_16;
         iter->j = 0;
         
   case ITER_COPY_SAMPLES_16:
         s = rwav->subchunk2size - iter->i;
         
         if (s > RWAV_ITERATE_BUF_SIZE)
            s = RWAV_ITERATE_BUF_SIZE;
         
         printf("copied %lu\n", s);
         u16 = (uint16_t *)rwav->samples;
         
         while (s != 0)
         {
            u16[iter->j++] = iter->data[44 + iter->i] | iter->data[45 + iter->i] << 8;
            iter->i += 2;
            s -= 2;
         }
         
         return iter->i < rwav->subchunk2size ? RWAV_ITERATE_MORE : RWAV_ITERATE_DONE;
      }
   }
   
   return RWAV_ITERATE_ERROR;
}

int rwav_load(rwav_t* out, const void* buf, size_t size)
{
   int res;
   rwav_iterator_t iter;
   
   rwav_init(&iter, out, buf, size);

   do
   {
      res = rwav_iterate(&iter);
   }
   while (res == RWAV_ITERATE_MORE);
   
   return res;
}

void rwav_free(rwav_t *rwav)
{
   free((void*)rwav->samples);
}
